package com.mf.utils;

import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import com.mf.entity.bo.exportExcel.ExportCollectFiledBo;
import com.mf.entity.bo.exportExcel.ExportColumnsBo;
import com.mf.entity.bo.exportExcel.ExportExcelBo;
import com.mf.entity.bo.exportExcel.ExportMergeRuleBo;
import com.mf.entity.vo.MergePropVo;
import com.mf.enums.ExportExcelType;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;

import java.math.BigDecimal;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

/**
 * @Author: lmf
 * @Create: 2024/6/26 15:03
 * Description:
 */
@Data
@Slf4j
public class ExportExcelUtils {
    /**
     * 获取表头
     * @param exportExcelBo 导出Excel的相关配置
     * @return 表头
     */
    public static List<List<String>> getHeader(ExportExcelBo exportExcelBo) {
        List<List<String>> headList = new ArrayList<>();
        for (ExportColumnsBo column : exportExcelBo.getColumns()) {
            headList.add(column.getTitle());
        }
        return headList;
    }


    /**
     * 设置导出数据的方法。
     * 该方法通过发送HTTP请求获取数据，并根据指定的字段集合处理和计算数据，最终将处理后的数据列表返回。
     *
     * @param exportExcelBo 导出Excel的相关配置，包括要导出的字段信息和计算汇总字段的配置。
     * @return 包含导出数据的列表，列表中的每个元素代表一行数据，如果需要，还包括汇总行。
     */
    public static List<List<Object>> setExportData(ExportExcelBo exportExcelBo, Object data) {
        if (data == null) {
            return null;
        }
        // 获取配置中的采集字段信息，用于后续的汇总计算
        List<ExportCollectFiledBo> collectFiled = exportExcelBo.getCollectFiled();

        // 初始化汇总字段的HashMap，用于存储每个汇总字段的计算结果
        HashMap<String, BigDecimal> sumFiled = new HashMap<>();
        if (collectFiled != null && !collectFiled.isEmpty()) {
            for (ExportCollectFiledBo s : collectFiled) {
                sumFiled.put(s.getProp(), BigDecimal.valueOf(0));
            }
        }

        // 初始化导出数据的列表
        List<List<Object>> exportList = new ArrayList<>();

        // 判断数据是否为JSONArray类型
        if (data instanceof JSONArray) {
            // 遍历数据数组，处理每条数据项
            for (Object item : new JSONArray(data)) {
                List<Object> itemList = new ArrayList<>();
                JSONObject entries = new JSONObject(item);
                // 遍历字段配置，获取每条数据的字段值，并进行汇总计算
                for (ExportColumnsBo column : exportExcelBo.getColumns()) {
                    Object value = entries.get(column.getProp());
                    // 对时间类型进行转换
                    if (column.getType() != null && ExportExcelType.TIME.value.equals(column.getType())) {
                        value = stringToDate((String) value, column.getFormat());
                    }
                    itemList.add(value);
                    // 判断当前字段是否需要进行汇总计算
                    isSumCollectFiled(sumFiled, column, entries, value);
                }
                exportList.add(itemList);
            }
            // 如果存在采集字段配置，添加汇总行到导出数据列表
            sumCollectFiled(sumFiled, exportExcelBo, collectFiled, exportList);
        }
        return exportList;
    }
    /**
     * 验证字段是否需要汇总计算
     */
    public static void isSumCollectFiled(HashMap<String, BigDecimal> sumFiled, ExportColumnsBo column,
                                         JSONObject entries, Object value) {
        if (sumFiled.get(column.getProp()) != null) {
            if (value instanceof BigDecimal) {
                BigDecimal quantity = (BigDecimal) entries.get(column.getProp());
                sumFiled.put(column.getProp(), sumFiled.get(column.getProp()).add(quantity));
            } else if (value instanceof Integer) {
                BigDecimal quantity = new BigDecimal((Integer) entries.get(column.getProp()));
                sumFiled.put(column.getProp(), sumFiled.get(column.getProp()).add(quantity));
            }
        }
    }

    /**
     * 汇总计算字段的方法。
     */
    public static void sumCollectFiled(HashMap<String, BigDecimal> sumFiled, ExportExcelBo exportExcelBo,
                                       List<ExportCollectFiledBo> collectFiled, List<List<Object>> exportList) {
        if (sumFiled.isEmpty() || collectFiled == null) {
            return;
        }
        List<Object> sumList = new ArrayList<>();
        // 初始化汇总行，填充null值以对齐字段
        for (int i = 0; i < exportExcelBo.getColumns().length; i++) {
            sumList.add(i == 0 ? "汇总" : null);
        }
        // 将每个汇总字段的计算结果添加到汇总行
        for (ExportCollectFiledBo s : collectFiled) {
            sumList.add(s.getColumnsIndex(), sumFiled.get(s.getProp()));
        }
        exportList.add(sumList);
    }
        /**
         * 将字符串转日期格式
         * @param dateString 时间格式字符串
         * @param format 时间格式
         * @return 时间格式字符串
         */
        public static String stringToDate (String dateString, String format){
            DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSX");
            ZonedDateTime zonedDateTime = ZonedDateTime.parse(dateString, formatter);
            // 转换为自定义的时间格式
            DateTimeFormatter outputFormatter = DateTimeFormatter.ofPattern(format);
            return zonedDateTime.format(outputFormatter);
        }

        /**
         * 根据导出Excel的业务对象和数据，获取合并规则列表。
         * 合并规则用于在Excel导出中合并单元格，以适应特定的数据格式。
         *
         * @param exportExcelBo 导出Excel的业务对象，包含合并规则等信息。
         * @param data 导出的数据，预期为JSON数组格式，用于根据数据内容确定合并范围。
         * @return 合并属性列表，每个合并属性描述了一段需要合并的单元格范围。
         */
        public static List<MergePropVo> getMergeRule (ExportExcelBo exportExcelBo, Object data){
            // 获取导出合并规则数组
            ExportMergeRuleBo[] mergeRule = exportExcelBo.getMergeRule();
            // 如果没有合并规则，则直接返回null
            if (mergeRule == null) {
                return null;
            }
            // 初始化合并属性列表
            List<MergePropVo> mergePropVoList = new ArrayList<>();
            // 判断数据是否为JSONArray类型
            if (data instanceof JSONArray) {
                // 将数据转换为JSONArray
                JSONArray jsonArray = new JSONArray(data);
                // 遍历合并规则数组
                for (ExportMergeRuleBo rule : mergeRule) {
                    // 遍历数据数组，寻找需要合并的单元格
                    for (int i = 0; i < jsonArray.size(); i++) {
                        int isMerge = 0; // 用于标记当前数据项是否需要合并
                        int mergeRowIndex = 0; // 记录合并起始行的索引
                        // 获取当前遍历到的数据项
                        JSONObject I_item = jsonArray.getJSONObject(i);
                        // 从当前数据项的下一个项开始，比较是否满足合并条件
                        for (int j = i + 1; j < jsonArray.size(); j++) {
                            JSONObject J_item = jsonArray.getJSONObject(j);
                            // 根据合并规则获取当前数据项的合并值
                            String mergeValue = getMergeValue(rule.getProp(), I_item);
                            // 如果当前数据项和下一个数据项的合并值相同，则需要合并
                            if (mergeValue.equals(getMergeValue(rule.getProp(), J_item))) {
                                // 如果是首次发现需要合并，则记录合并起始行的索引
                                if (isMerge == 0) {
                                    mergeRowIndex = j;
                                }
                                isMerge++; // 合并计数加一
                            } else if (isMerge != 0) {
                                // 如果不再满足合并条件，说明当前合并范围结束
                                setMergeProp(mergeRowIndex, j, rule, mergePropVoList);
                                // 更新遍历索引，跳过已处理的数据项
                                i = j - 1;
                                break;
                            }

                            // 如果当前项是数组的最后一项，且存在合并需求，则处理最后的合并范围
                            if (j == jsonArray.size() - 1 && isMerge != 0) {
                                int endIndex = j;
                                // 对于最后一行，需要手动调整结束索引，以包含最后一项
                                if (j == jsonArray.size() - 1) {
                                    endIndex++;
                                }
                                setMergeProp(mergeRowIndex, endIndex, rule, mergePropVoList);
                                i = j - 1;
                                break;
                            }
                        }
                    }
                }
            }
            return mergePropVoList; // 返回合并属性列表
        }


        /**
         * 设置合并属性
         * 此方法用于创建并配置一个MergeProp对象，该对象表示了表格中的一段合并区域。
         * 它通过给定的起始行索引、结束行索引和列索引，定义了合并的范围和位置。
         * 创建完成后，该MergeProp对象被添加到mergePropList中。
         *
         * @param mergeRowIndex 合并区域的起始行索引
         * @param j 合并区域的结束行索引
         * @param rule 合并规则对象，包含列索引等信息
         * @param mergePropVoList 存储所有合并属性的对象列表
         */
        public static void setMergeProp ( int mergeRowIndex, int j, ExportMergeRuleBo
        rule, List <MergePropVo> mergePropVoList){
            MergePropVo mergePropVo = new MergePropVo();
            mergePropVo.setRowStartIndex(mergeRowIndex);
            mergePropVo.setRowEndIndex(j);
            mergePropVo.setColumnIndex(rule.getColumnsIndex());
            mergePropVoList.add(mergePropVo);
        }

        /**
         * 获取合并单元格对比的值
         *
         * @param prop 属性名数组，表示需要合并的单元格的值字段名称
         * @param item JSON对象，包含实际的数据
         * @return 返回拼接后的字符串，表示是否合并单元格对比的值
         */
        private static String getMergeValue (String[]prop, JSONObject item){
            StringBuilder stringBuilder = new StringBuilder();
            for (String s : prop) {
                if (!item.isNull(s)) {
                    stringBuilder.append(item.get(s));
                }
            }
            return stringBuilder.toString();
        }


    }
